
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.IO;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;
using Swaf.Security;

using UserLogin;

using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Core;


namespace Articles
{
	/// <summary>
    /// These bizrules provide non-read only access for the gallery.  Read only bizrules are
    /// provided in the Gallery.BusinessRules class.  Every bizrule in this class is capable of
    /// throwing a ArticlesException when runtime failures occur.
    /// All of the bizrules in this class use the current user from the security context it is being
    /// called in as  needed for user specific or company specific actions.  It is not possible to 
    /// provide user id or company id for these bizrules in order to take action on a different 
    /// user/company.
    /// </summary>
    public class ArticleAdminBizRules : Syrinx.BaseBizRules
    {
		public static string s_objectTreeName = "Articles";

		
		public void movePositionUp(IApplication app, IRuleContext context, IBizObj obj)
		{
			if (obj != null && (int)obj.get("position") > 1 )
			{
				obj.put("position", (int)obj.get("position") - 1);
				app.appData.put("DA.ArticlesAdmin.UpdateArticleSetPosition", obj);
			}
		}
		public void movePositionDown(IApplication app, IRuleContext context, IBizObj obj)
		{
			if (obj != null)
			{
				obj.put("position", (int)obj.get("position") + 1);
				app.appData.put("DA.ArticlesAdmin.UpdateArticleSetPosition", obj);
			}
		}
		

		public void updateArticleTranslation(IApplication app, IRuleContext context, IBizObj article, Swaf.BGuid.Guid translationId)
		{
			try
			{
                INameAddressSupport ns = app.securityMgr.currentPrincipal.additionalInfo;
				
				//calcArticleLangTranslation(ref article, translation);
				IResults rc = app.ruleMgr.execute("getAllTranslations");
				if(rc.ExceptionInfo!=null)
					throw new ArticlesException("error executing updateArticleTranslation.",rc.ExceptionInfo);
				IBizObj translation = ((IBizObjList)rc.CallResults).search("id", translationId);
				if (translation != null)
				{
					string[] fields = new string[] { "translatedTitles", "translatedSummaries", "translatedBodies", "translatedCaptions" };
					string[] updatedfields = new string[] { "titleTranslation", "summaryTranslation", "bodyTranslation", "captionTranslation" };
					for (int i = 0; i < fields.Length; i++)
					{
						string updatedVal = (string)article.get(updatedfields[i]);
						IBizObjList list = article.get(fields[i]) as IBizObjList;
						if (list != null && !Cmn.IsEmpty(updatedVal))
						{
							IBizObj bizobj = list.search("transId", translation.get("id"));
							if (bizobj == null)
							{
								bizobj = ((IBizObjList)article.get(fields[i])).insertNew();
								bizobj.put("transId", translation.get("id"));
							}
							bizobj.put("val", article.get(updatedfields[i]));
						}
					}

					IBizObj details = ((IBizObjList)article.get("translationDetails")).search("transId", translationId);
					if (details == null)
					{
						details = ((IBizObjList)article.get("translationDetails")).insertNew();
						details.put("transId", translationId);
					}
					if (details != null)
					{
						details.put("ud", DateTime.Now.ToUniversalTime());
						details.put("uUser", CurrentUser.get("id"));
					}


					app.appData.put("DA.ArticlesAdmin.UpdateArticleTranslation", article);
				}
			}
			catch (Exception e)
			{
				throw new ArticlesException("error executing updateArticleTranslation",e);
			}
		}
		/// <summary>
		/// this bizrule returns a null bizobj if the check passes the requirement.
		/// (e.g. the user still meets the limit that is set on it.)
		/// </summary>
		/// <param name="app"></param>
		/// <param name="context"></param>
		/// <returns></returns>
		public IBizObj canCreateArticle(IApplication app, IRuleContext context)
		{
			try
			{
				IResults rc = app.ruleMgr.execute("getArticleLimit", CurrentUser);
				IBizObj bizobj = null;
				
				if (rc.ExceptionInfo != null)
					throw new ArticlesException("error executing getArticleLimit in checkCreateLimit bizrule.", rc.ExceptionInfo);
				IBizObj limit = rc.CallResults as IBizObj;
				if (Convert.ToInt32(limit.get("maxTotalArticles")) <= Convert.ToInt64(limit.get("currentArticleCount")))
					return limit;
				else
				{
					
					FlexiMap m = new FlexiMap();
					m["id"] = ActiveCompanyId;
					Int64 currentCount = 0;
					bool keepProcessing = true;
					while (keepProcessing)
					{
						bizobj = app.appData.get("DA.ArticlesAdmin.GetCompanyArticleCount", m) as IBizObj;
						currentCount += Convert.ToInt64(bizobj.get("currentArticleCount"));
						rc = app.ruleMgr.execute("getArticleLimit", bizobj);
						if (rc.ExceptionInfo != null)
							throw new ArticlesException("error executing getArticleLimit in checkCreateLimit bizrule.", rc.ExceptionInfo);
						bizobj = rc.CallResults as IBizObj;
						if(bizobj==null)
							throw new ArticlesException("error executing getArticleLimit in checkCreateLimit bizrule.", rc.ExceptionInfo);

						bizobj.put("currentArticleCount", currentCount);
						
						if (currentCount >= Convert.ToInt32(bizobj.get("maxTotalArticles")) && Convert.ToInt32(bizobj.get("maxTotalArticles")) != -1)
							keepProcessing = false;
						else
						{
							rc = app.ruleMgr.execute("getCompany", bizobj.get("id") as Swaf.BGuid.Guid);
							if (rc.ExceptionInfo != null)
								throw new ArticlesException("error executing getArticleLimit in checkCreateLimit bizrule.", rc.ExceptionInfo);
							IBizObj obj = rc.CallResults as IBizObj;
							if (obj.get("parentId") == null)
								if (bizobj.get("maxTotalArticles").Equals(-1))
									return null;
								keepProcessing = false;
							m["id"] = obj.get("parentId");
						}
					}
				}
				return bizobj;
			}
			catch (ArticlesException ae) { throw; }
			catch (Exception e)
			{
				throw new ArticlesException("error executing checkCreateLImit bizrule", e);
			}
		}
		
		public IBizObj getArticleLimit(IApplication app, IRuleContext context, IBizObj bizobj)
		{
			try
			{
				bool isUser = "User".Equals(bizobj.type.Name);
				IBizObj obj = app.appData.get(string.Format("DA.ArticlesAdmin.{0}",
					isUser ? "GetUserArticleLimit" : "GetCompanyArticleLimit"), bizobj) as IBizObj;
				if (obj == null)
					obj = App.bizObjMgr.create(isUser ? "UserLimits" : "CompanyLimits");
				return obj;
			}
			catch (Exception e)
			{
				throw new ArticlesException("error executing getArticleLimit bizrule", e);
			}
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="app"></param>
		/// <param name="context"></param>
		/// <param name="limit"></param>
		/// <remarks>this rule needs to be on its on class. maybe a full Limits Feature</remarks>
		public void updateArticleLimit(IApplication app, IRuleContext context, IBizObj limit)
		{
			try
			{
				app.appData.put("DA.ArticlesAdmin.DeleteArticleLimit", limit);
				app.appData.put("DA.ArticlesAdmin.AddArticleLimit",limit);
			}
			catch (Exception e)
			{
				throw new ArticlesException("error executing updateArticleLimit bizrule", e);

			}
		}
			 
		public void setArticleSetHighlightPicture(IApplication app, IRuleContext context, IBizObj articleSet)
		{
			try
			{
				if (articleSet == null && articleSet.containsField("highlightPicture"))
					throw new ArticlesException("error executing setArticleSetHighLightPicture bizrule. Param is null.");
				app.appData.put("DA.ArticlesAdmin.SetHighlightPicture", articleSet);
			}
			catch (Exception e)
			{
			}
			
			
		}
		public void addArticlePrimaryMedia(IApplication app, IRuleContext context, IBizObj picture, Swaf.BGuid.Guid articleId)
		{
			
		}
		public void addPrimaryMedia(IApplication app, IRuleContext context, string mediaName, Swaf.BGuid.Guid articleId)
		{
			try
			{
				FlexiMap m = new FlexiMap();
				m.put("articleId", articleId);
				m.put("primaryMediaName",mediaName);
				m.put("dateUpdated", DateTime.Now.ToUniversalTime());
				app.appData.put("DA.ArticlesAdmin.SetPrimaryPicture", m);
				
			}
			catch (Exception e)
			{

			}
		}

        public void moveArticlesToNewParent(IApplication app, IRuleContext context, object parentId, IBizObjList articles)
        {
            Swaf.BGuid.Guid gpId = parentId as Swaf.BGuid.Guid;
            if (gpId == null)
                gpId = Swaf.BGuid.Guid.TryParse((string)parentId);

            if (gpId != null)
                foreach (IBizObj art in articles)
                {
                    IResults rc = context.RuleGroup.execute(context, "getArticleDetail", art.get("id"), false, DetailLevel.fullDetails, false, false, null);
                    IBizObj artDet = rc.CallResults as IBizObj;
                    if (artDet != null)
                    {
                        app.Log.println(Swaf.Logging.LoggingLevels.Medium, "moveArticlesToNewParent bizrule - Moving article: {0} - {1}", art.get("id"), art.get("baseTitle"));
                        artDet.put("parentId", gpId);
                        app.ruleMgr.execute(context, "moveBizObjInTree", artDet, true);
                    }
                    else
                        app.Log.println(Swaf.Logging.LoggingLevels.Medium, "moveArticlesToNewParent bizrule - Invalid article id: {0}", art.get("id"));

                }
            else
                app.Log.println(Swaf.Logging.LoggingLevels.Medium, "moveArticlesToNewParent bizrule - Invalid parent id: {0}", parentId);

        }
		
		public void updateArticle(IApplication app, IRuleContext context, IBizObj article)
        {
            //Temp test code for restore bug fix -------------------
            string body = (string)article.get("baseBody");
            bool containsText = body.Contains("I am not sure of any accuracy but I read from some internet source");
            if (containsText)
            {
                containsText = !containsText;
            }
            //------------------------------------------------------
			bool inRestore = (context.get("doingArticleRestore") is bool && (bool)context.get("doingArticleRestore"));
			if (article == null)
                throw new ArticlesException("The article bizobj must not be null");
			if (article.isFieldSet("titleTranslation") || article.isFieldSet("summaryTranslation") || article.isFieldSet("bodyTranslation"))
			{
				updateArticleTranslation(app, context, article,(Swaf.BGuid.Guid) article.get("translationId"));
				if (!article.isNew)
					return;
			}
			if (Cmn.IsEmpty(article.get("treeName")))
				article.put("treeName", s_objectTreeName);
            try
			{
				FlexiMap m = new FlexiMap();
				object pid = article.get("parentId");
				if (pid is string)
				{
					m.put("uniqueTitle", pid);
					m.put("companyId", ActiveCompanyId);
					IBizObj parent = app.appData.get("DA.Articles.GetArticleIdByUniqueTitle", m) as IBizObj;
					if (parent != null)
						pid = parent.get("id");
				}
				if (!article.isFieldSet("companyId") && article.get("companyId") == null)
					article.put("companyId", ActiveCompanyId);
				else
				{
					//EXTREME TODO: Ensure that the user is actually authorized to update this article and can work with
					//company that was contained in the article.companyId field.
				}
				if (!inRestore)
				{
					article.put("counts.dateUpdated", DateTime.UtcNow);
					if (article.isNew || DateTime.MinValue.CompareTo(article.get("counts.dateCreated")) == 0 || article.get("counts.dateCreated") == null)
						article.put("counts.dateCreated", DateTime.UtcNow);
				}
				if(article.get("parentId") != null && (article.get("parentId").Equals(app.appData.get("ArticleParentDropDown-Root.id")) || article.get("parentId").Equals(Swaf.BGuid.Guid.Empty)))
					article.put("parentId", null);
				if ("-".CompareTo(article.get("suggestedDisplayType")) == 0)
					article.put("suggestedDisplayType", "");
				app.appData.put(string.Format("DA.ArticlesAdmin.{0}", article.isNew ? "AddArticle" : "UpdateArticle"), article);
                if (inRestore)
                {
                    app.appData.put("DA.ArticlesAdmin.UpdateArticleTranslation", article);
                }
				bool checkToReparent = CurrentPrincipal.getPrivilege("AppFunctions.ReparentArticles") > Privileges.ReadOnly;

				//This code block will reset the cached uniqueTitle for this article's unique title to ensure it
				//has the right value now.  If the id was being used by another article, it will get reset to this article.
				if (!Cmn.IsEmpty(article.get("uniqueTitle")))
				{
					FlexiMap m2 = app.appData.get("cachedUniqueTitles") as FlexiMap;
					if (m2 != null)
					{
						FlexiMap titleMap = m2.get(ActiveCompanyId.ToString()) as FlexiMap;
						if (titleMap != null)
							titleMap[article.get("uniqueTitle").ToString()] = article.get("id");
					}
				}

				//TODO: If the user reparented with UI, but they dont have the right to reparent,
				//we should notify the user that the article was not reparented, but we dont want to do that
				//with an exception.  We should have a formal way to send back an information message that is like
				//an exception but is not.

				context.RuleGroup.execute(context, "updateBizObjTreeNode", article, checkToReparent, article.get("counts.dateUpdated"), article.get("counts.author.id"));
            }
            catch (Exception e)
            {
                throw new ArticlesException("Error executing updatePictureInfo bizRule", e);
            }
        }

		public void generateArticleFromMedia(IApplication app, IRuleContext context, object parentObjId, IBizObjList mediaList)
		{
			FlexiMap m = new FlexiMap();
			Swaf.BGuid.Guid parentId = parentObjId as Swaf.BGuid.Guid;
			string parentName = parentObjId as string;
			if (parentId == null)
				parentId = Swaf.BGuid.Guid.TryParse(parentName);
			if (parentId == null)
			{
				m.put("uniqueTitle", parentName);
				m.put("companyId", ActiveCompanyId);
				m.put("listDetailLevel", DetailLevel.idOnly);
				IBizObj obj = app.appData.get("DA.Articles.GetArticleIdByUniqueTitle", m) as IBizObj;
				if (obj != null)
					parentId = obj.get("id") as Swaf.BGuid.Guid;
			}
			if (parentId != null)
			{
				IBizObj article = app.bizObjMgr.create("Article");
				article.put("parentId", parentId);
				article.put("treeName", s_objectTreeName);
				foreach (IBizObj media in mediaList)
				{
					article.put("id", new Swaf.BGuid.Guid());
					article.put("baseTitle", media.get("name"));
					article.put("primaryPicture.name", media.get("name"));
					IResults rc = app.ruleMgr.execute("updateArticle", article);
				}
			}
		}

		public void generateArticleFromMediaDirectory(IApplication app, IRuleContext context, string cacheName, object parentId, string path)
		{
			FlexiMap m = new FlexiMap();
			bool isRoot = parentId == null;

			m.put("parentId", parentId);
			m.put("includeSelf", true);
			m.put("listDetailLevel", DetailLevel.basicDetails);

			IRuleGroup rules = app.ruleMgr.executeAsync("getArticles", null, null, new object[] { m, (IBizObj)null });
			rules.execute("getMediaItemsFromDirectory", null, null, new object[] { cacheName, MediaCache.ImageListType.filesOnly, path });
			rules.execute("getMediaLocationDirectories", null, null, new object[] { cacheName, true, path });
			rules.waitForCompletion(false);
					

			IBizObjList articles = rules.getCallResults(0) as IBizObjList;
			IBizObjList images = rules.getCallResults(1) as IBizObjList;
			IBizObjList childDirs = rules.getCallResults(2) as IBizObjList;
			
			if (articles != null)
			{
				//create the folder as parentarticle
				IBizObj article =articles.search("primaryPicture.name", path) ;
				IResults rc = null;
				if (article == null)
				{
					if (!isRoot)
					{
						Swaf.BGuid.Guid aid = new Swaf.BGuid.Guid();
						article = articles.insertNew();
						article.put("id", aid);
						article.put("parentId", parentId);
						article.put("baseTitle", path);
						article.put("uniqueTitle", path);
						article.put("primaryPicture.name", path);
						article.put("parentId", parentId);
						article.put("treeName", s_objectTreeName);
						rc = app.ruleMgr.execute("updateArticle", article);
						parentId = aid;
					}
				}
				else
					parentId = article.get("id");

				if (images != null)
				{
					string fullname = "";
					foreach (IBizObj image in images)
					{
						fullname = image.get("name").ToString();
						if (articles.search("primaryPicture.name", fullname) == null)
						{
							IBizObj obj = App.bizObjMgr.create("Article");
							obj.put("id", new Swaf.BGuid.Guid());
							obj.put("baseTitle", image.get("name"));
							obj.put("uniqueTitle", image.get("name"));
							obj.put("primaryPicture.name", image.get("name"));
							obj.put("parentId", parentId);
							obj.put("treeName", s_objectTreeName);
							rc = app.ruleMgr.execute("updateArticle", obj);
							if (rc.ExceptionInfo != null)
								throw new ArticlesException(string.Format("error adding article {0}", obj.Xml), rc.ExceptionInfo);
						}
					}
					if (childDirs.numElements > 0)
						foreach (IBizObj dir in childDirs[0].get("childDirs") as IBizObjList)
							context.RuleGroup.execute("generateArticleFromImages", null, null, new object[] { cacheName, parentId, string.Format("{0}/{1}", path, dir.get("name")) });
				}
			}			
		}

		public void undeleteArticle(IApplication app, IRuleContext context, Swaf.BGuid.Guid articleId)
		{
			IResults rc = context.RuleGroup.execute(context, "getArticleDetail", articleId, false, DetailLevel.idAndTitle, false, false, null);
			IBizObj article = rc.CallResults as IBizObj;
			if (article != null)
			{
				string treeName = (string)article.get("treeName");
				if (treeName.StartsWith("Deleted"))
				{
					string baseTreeName = treeName.Substring("Deleted".Length);
					FlexiMap m = new FlexiMap();
					m["id"] = articleId;
					m["deletedTreeName"] = "Deleted" + baseTreeName;
					int c = (int)app.appData.get("DA.Articles.UpdateArticlesRemoveDeleteFromTreeName", m);
				}
			}
		}
        /// <summary>
        /// This bizrule deletes the given album Picture from the data store. It does not delete the
        /// given image file from the media cache. When this rule is called it will remove the
        /// picture information and the ForumThread created for the given Picture. 
        /// </summary>
        /// <param name="picture">The picture to be deleted from the data store.</param>
		public void deleteArticle(IApplication app, IRuleContext context, Swaf.BGuid.Guid articleId, bool deepDelete)
        {
            try
            {
				IResults rc = context.RuleGroup.execute(context, "getArticleDetail", articleId, false, DetailLevel.idAndTitle, false, false, null);
				IBizObj article = rc.CallResults as IBizObj;
				if (article != null)
				{
					string treeName = (string)article.get("treeName");
					if (treeName.StartsWith("Deleted"))
					{
						//rc = context.RuleGroup.execute(context, "decrecrementParentNodeCounts", articleId);
						IBizObj nodeInfo = rc.CallResults as IBizObj;
						IBizObjList childNodes = null;
						if (deepDelete)
							childNodes = app.ruleMgr.execute(context, "getBizObjTreeChildren", articleId, deepDelete).CallResults as IBizObjList;

						//app.ruleMgr.execute("deleteForumThread", article.get("id") as Swaf.BGuid.Guid);
						rc = app.ruleMgr.execute(context, "removeBizObjFromTree", articleId, deepDelete, false);
						if (rc.ExceptionInfo != null)
							throw rc.ExceptionInfo;

						FlexiMap m = new FlexiMap();
						m.put("id", articleId);
						app.appData.put("DA.ArticlesAdmin.DeleteArticle", m);
						if (childNodes != null)
							foreach (IBizObj child in childNodes)
								app.appData.put("DA.ArticlesAdmin.DeleteArticle", child);

						//TEMP: Add back when done with bulk deletes
						//context.RuleGroup.execute(context, "refreshLatestUpdatedForParents", nodeInfo.get("parentId"));
					}
					else
					{
						FlexiMap m = new FlexiMap();
						m["id"] = articleId;
                        m["dateUpdated"] = DateTime.UtcNow;
						int c = (int)app.appData.get("DA.Articles.UpdateArticlesAddDeleteToTreeName", m);
                        rc = context.RuleGroup.execute(context, "decrecrementParentNodeCounts", articleId);
					}
				}
			}
            catch (Exception e)
            {
				throw new ArticlesException("Error executing deleteArticle bizRule.", e);
            }
        }

        /// <summary>
        /// This bizrule deletes the given bizobjlist of album Pictures from the data store by looping
        /// throught the list and calling the deletePicture bizrule.
        /// </summary>
        /// <param name="pictures">The bizobjlist of album Pictures to remove from the data store.</param>
		public void deleteArticles(IApplication app, IRuleContext context, IBizObjList articles)
        {
			if (articles == null)
                throw new ArticlesException("The pictures bizobjlist must not be null");

            try
            {
				foreach (IBizObj obj in articles)
                    app.ruleMgr.execute("deletePicture", obj);
            }
            catch (Exception e)
            {
                throw new ArticlesException("Error executing deletePicture bizRule.", e);
            }
        }

		public IBizObj getUserArticleLimit(IApplication app, IRuleContext context, object returnValue, object[] inputParams)
		{
			if (returnValue != null)
			{
				IBizObj obj = returnValue as IBizObj;
				obj = getArticleLimit(app, context, obj);
				return obj;
			}
			return null;
		}
		public IBizObj getCompanyArticleLimits(IApplication app, IRuleContext context, object returnValue, object[] inputParams)
		{
			IBizObj bizobj = returnValue as IBizObj;
			if (bizobj != null)
			{
				IBizObj limits = getArticleLimit(app, context, bizobj);
				if (limits == null)
					limits = app.bizObjMgr.create("CompanyLimits");
				bizobj.put("limits", limits);
			}
			return bizobj;
		}

		public void updateCompanyArticleLimits(IApplication app, IRuleContext context, object returnValue, object[] inputParams)
		{
			try
			{
				IBizObj bizobj = inputParams[2] as IBizObj;
				if (bizobj != null && bizobj.containsField("limits"))
				{
					IBizObj limit = bizobj.get("limits") as IBizObj;
					if (limit.isNew)
						limit.put("id", bizobj.get("id"));
					IResults rc = app.ruleMgr.execute(context, "updateArticleLimit", limit);
					if (rc.ExceptionInfo != null)
						throw new ArticlesException("error executing updateArticleLImits from updateCompanyLimits", rc.ExceptionInfo);
				}
			}
			catch (Exception e)
			{
				throw new ArticlesException("error executing updateCompanyLimits", e);
			}
		}


		public void backupCompanyArticles(IApplication app, IRuleContext context, string treeName, IBizObj deployInfo, ZipOutputStream zOut)
		{

			try
			{
				Dictionary<string, int> articleTitles = new Dictionary<string, int>();
				Debug.Assert(zOut != null);

				if (deployInfo == null)
					throw new ArticlesException("required parameter is missing. error executing createArticleBackup bizrule.");

				bool full = (bool)deployInfo.get("fullCompanyBackup");
				string option = (string)deployInfo.get("articleLevel");
				if (full || (!Cmn.IsEmpty(option) && option.CompareTo("none") != 0))
				{
					bool includeNonSys = full || option.CompareTo("sysOnly") != 0;
					bool includeSys = full || option.CompareTo("cmpOnly") != 0;
					bool useDateSince = deployInfo.get("backupSince") != null;
					DateTime dateSince = DateTime.MinValue;
					if(useDateSince)
						dateSince = (DateTime)deployInfo.get("backupSince");
                    if (treeName == null)
                        treeName = "Articles";

					foreach (IBizObj company in deployInfo.get("backupCompanies") as IBizObjList)
					{
						FlexiMap info = new FlexiMap();
                        info.put("companyId", company.get("id") as Swaf.BGuid.Guid);
                        info.put("objectTreeName", treeName);
                        info.put("listDetailLevel", DetailLevel.fullDetails);
                        info.put("includeTranslations", true);
                        info.put("defaultCacheName", company.get("settings.defaultMediaCache"));
                        info.put("includeSelf", true);

                        if (useDateSince)
                        {
                            info.put("entireTree", true);
                            IBizObj filterSortInfo = app.bizObjMgr.create("FilterSortOptions");
                            IBizObjList filterInfo = (IBizObjList)filterSortInfo.get("filterOptions");
                            IBizObj filter = filterInfo.insertNew();
                            filter.put("field", "nc.dateUpdated");
                            filter.put("criteria", ">");
                            filter.put("value", dateSince);

                            IResults rc = app.ruleMgr.execute("getArticles", info, filterSortInfo);
                            IBizObjList articlesUpdated = rc.CallResults as IBizObjList;
                            if (articlesUpdated != null && articlesUpdated.numElements > 0)
                            {
                                zOut.PutNextEntry(new ZipEntry(string.Format("data{0}{1}{0}Articles{0}{2}.xml", Path.DirectorySeparatorChar, company.get("shortName"), treeName)));
                                TextWriter t = (TextWriter)context.get("zipWriter");
                                t.Write("<?xml version=\"1.0\" ?>\n" + articlesUpdated.Xml.ToString());
                                t.Flush();
                            }
                        }
                        else
                        {
                            info.put("companyId", company.get("id") as Swaf.BGuid.Guid);
                            info.put("objectTreeName", treeName == null ? "Articles" : treeName);
                            info.put("includeSelf", true);
                            info.put("listDetailLevel", DetailLevel.fullDetails);
                            info.put("includeTranslations", true);
                            info.put("defaultCacheName", company.get("settings.defaultMediaCache"));

                            IResults rc = app.ruleMgr.execute("getArticles", info, (IBizObj)null);
                            IBizObjList books = rc.CallResults as IBizObjList;

                            //process each book as xml
                            if (books != null)
                                foreach (IBizObj book in books)
                                {
                                    bool isSysArt = (bool)book.get("isSystem");
                                    if ((isSysArt && includeSys) || (!isSysArt && includeNonSys))
                                    {
                                        info.put("parentId", book.get("id"));
                                        info.put("entireTree", true);
                                        rc = app.ruleMgr.execute("getArticles", info, (IBizObj)null);
                                        IBizObjList fullBook = rc.CallResults as IBizObjList;
                                        IBizObjList actualBook = app.bizObjMgr.createList("Article");
                                        if (fullBook != null)
                                        {
                                            foreach (IBizObj art in fullBook)
                                                if (!useDateSince || (art.get("counts.dateUpdated") != null && (DateTime)art.get("counts.dateUpdated") > dateSince))
                                                    actualBook.Add(art);

                                            if (actualBook.numElements > 0)
                                            {
                                                string bookZipEntryName = (string)book.get("baseTitle");
                                                int numWithTitle = 0;
                                                articleTitles.TryGetValue(bookZipEntryName, out numWithTitle);
                                                articleTitles[bookZipEntryName] = ++numWithTitle;

                                                if (numWithTitle > 1)
                                                    bookZipEntryName += "-" + numWithTitle;

                                                zOut.PutNextEntry(new ZipEntry(string.Format("data{0}{1}{0}Articles{0}{2}.xml", Path.DirectorySeparatorChar, company.get("shortName"), bookZipEntryName)));
                                                TextWriter t = (TextWriter)context.get("zipWriter");
                                                t.Write("<?xml version=\"1.0\" ?>\n" + actualBook.Xml.ToString());
                                                t.Flush();
                                            }
                                        }
                                    }
                                }
                        }
					}
				}
			}
			catch (Exception e)
			{
				throw new ArticlesException("error executing createTemplateDataBackup.", e);
			}
		}
		public void postExtendArticleexecuteCompanyBackup(IApplication app, IRuleContext context, object returnValue, object[] inputParams)
		{

			try
			{
				INameAddressSupport deployInfo = inputParams[2] as INameAddressSupport;
				ZipOutputStream zOut = inputParams[3] as ZipOutputStream;

				IResults rc = app.ruleMgr.execute(context, "backupCompanyArticles", "Articles", deployInfo, zOut);
			}
			catch (Exception e)
			{
				throw new ArticlesException("error executing postExtendProductCreateDeployment.", e);
			}
		}

		public void postExtendArticleexecuteCompanyRestore(IApplication app, IRuleContext context, object returnValue, object[] inputParams)
		{
			try
			{
				INameAddressSupport backup = inputParams[2] as INameAddressSupport;
				ZipFile zip = inputParams[3] as ZipFile;
				IResults rc = app.ruleMgr.execute(context, "restoreCompanyArticles", backup, zip);
				if (rc.ExceptionInfo != null)
					throw rc.ExceptionInfo;
			}
			catch (Exception e)
			{
				throw new ArticlesException("error executing postExtendProductCreateDeployment.", e);
			}
		}

		public IBizObj findArticle(IApplication app, IRuleContext context, Swaf.BGuid.Guid companyId, Swaf.BGuid.Guid articleId, string uniqueTitle, string keyName, string treeName, bool doGeneralFind)
		{
			FlexiMap m = new FlexiMap();
			m.put("companyId", companyId);
			m.put("id", articleId);
			m.put("uniqueTitle", uniqueTitle);
			m.put("keyName", keyName);
			m.put("treeName", treeName);
            m.put("details", DetailLevel.idAndTitlePlus);
            IBizObj article = app.appData.get("DA.Articles.GetArticleDetails", m) as IBizObj;
            if (article == null)
            {
                article = app.appData.get("DA.Articles.GetArticleIdByUniqueTitle", m) as IBizObj;
                if (article != null)
                    article = app.appData.get("DA.Articles.GetArticleDetails", article) as IBizObj;
                else if(doGeneralFind)
                    article = app.appData.get("DA.Articles.FindArticleIdByOneOfId", m) as IBizObj;
            }
            return article;
		}

		public void deleteAllCompanyArticles(IApplication app, IRuleContext context, Swaf.BGuid.Guid companyId)
		{

		}

		public void restoreCompanyArticles(IApplication app, IRuleContext context, IBizObj bkInfo, ZipFile zipFile)
		{
			IBizObjList restoreCompanies = (IBizObjList)bkInfo.get("restoreCompanies");
			IBizObjList backupCompanies = (IBizObjList)bkInfo.get("backupCompanies");
			//We need to change the active company id to the company that the backup is for so that
			//any reference to the activeCompanyId in the following company will point to the correct company.
			Swaf.BGuid.Guid oldCmpId = ActiveCompanyId;

			try
			{
				for (int pos = 0; pos < restoreCompanies.numElements; ++pos)
				{
					IBizObj restoreCompany = restoreCompanies[pos];
					IBizObj backupCompany = backupCompanies.numElements == 1 ? backupCompanies[0] : backupCompanies[pos];
					bool restoreToSameCompany = string.Compare((string)backupCompany.get("shortName"), (string)restoreCompany.get("shortName")) == 0;
					ActiveCompanyId = restoreCompany.get("id") as Swaf.BGuid.Guid;

					bool deleteExistingFirst = (bool)bkInfo.get("deleteExistingArticles");
					if (deleteExistingFirst)
					{
					}

					try
					{
						Dictionary<Swaf.BGuid.Guid, Swaf.BGuid.Guid> m_idDict = context.get("IdMap" + restoreCompany.get("shortName")) as Dictionary<Swaf.BGuid.Guid, Swaf.BGuid.Guid>;
						if (m_idDict == null)
							context.put("IdMap" + restoreCompany.get("shortName"), m_idDict = new Dictionary<Swaf.BGuid.Guid, Swaf.BGuid.Guid>());

						string path = bkInfo.get("filePath").ToString();
						string desiredArticlePath = string.Format("data{0}{1}{0}Articles{0}", Path.DirectorySeparatorChar, backupCompany.get("shortName"));
						foreach (ZipEntry entry in zipFile)
						{
							string entryName = entry.Name.Replace('/','\\');
							if (entry.IsFile && entryName.StartsWith(desiredArticlePath))
							{
								StreamReader sr = new StreamReader(zipFile.GetInputStream(entry));
								string file = sr.ReadToEnd();
								IBizObjList articles = app.bizObjMgr.createList(SourceType.BasicSource, file, "Article", null);
								foreach (IBizObj article in articles)
								{
                                    //Temp test code for restore bug fix -------------------
                                    string body = (string)article.get("baseBody");
                                    bool containsText = body.Contains("I am not sure of any accuracy but I read from some internet source");
                                    if (containsText)
                                    {
                                        containsText = !containsText;
                                    }
                                    //------------------------------------------------------

                                    
                                    bool restoreArtOverwriteChanges = false;
									bool restoreArtBringBackDeleted = false;
									IBizObj existArt = context.RuleGroup.execute(context, "findArticle",
                                        restoreCompany.get("id"), article.get("id"), article.get("uniqueTitle"), article.get("keyName"), article.get("treeName"), !restoreToSameCompany).CallResults as IBizObj;
									IBizObj existParent = null;
									if (existArt == null)
									{
										if (bkInfo.get("backupSince") != null && article.get("counts.dateCreated") != null)
										{
											DateTime restoreDateSince = (DateTime)bkInfo.get("backupSince");
											DateTime artCreatedDate = (DateTime)article.get("counts.dateCreated");
											if (artCreatedDate < restoreDateSince)
												restoreArtBringBackDeleted = true;
										}
										string parentKeyName = (string)article.get("keyName");
										int lastIndex;
										while ((lastIndex = parentKeyName.LastIndexOf('\xd')) != -1 && existParent == null)
											existParent = context.RuleGroup.execute(context, "findArticle",
												restoreCompany.get("id"), null,
                                                null, parentKeyName = parentKeyName.Substring(0, lastIndex), article.get("treeName"), true).CallResults as IBizObj;
										if (existParent != null)
										{
											existArt = context.RuleGroup.execute(context, "findArticle",
												restoreCompany.get("id"), article.get("id"), article.get("uniqueTitle"),
                                                parentKeyName + "\xd" + article.get("name"), article.get("treeName"), !restoreToSameCompany).CallResults as IBizObj;
										}
									}
									else
									{
										if (bkInfo.get("backupSince") != null && existArt.get("counts.dateUpdated") != null)
										{
											DateTime restoreDateSince = (DateTime)bkInfo.get("backupSince");
											DateTime existArtUpdateDate = (DateTime)existArt.get("counts.dateUpdated");
											if (existArtUpdateDate > restoreDateSince)
												restoreArtOverwriteChanges = true;
										}
									}
									article.setAllFieldsDirty();

									object parentId = existArt == null ? existParent == null ? null : existParent.get("id") : existArt.get("parentId");
									if (!m_idDict.ContainsKey(article.get("id") as Swaf.BGuid.Guid))
										m_idDict.Add(article.get("id") as Swaf.BGuid.Guid, (Swaf.BGuid.Guid)(existArt != null ? existArt.get("id") : new Swaf.BGuid.Guid()));

									if (!m_idDict.ContainsKey(article.get("parentId") as Swaf.BGuid.Guid))
										m_idDict.Add(article.get("parentId") as Swaf.BGuid.Guid, parentId as Swaf.BGuid.Guid);

									if (!m_idDict.ContainsKey(article.get("objectId") as Swaf.BGuid.Guid))
										m_idDict.Add(article.get("objectId") as Swaf.BGuid.Guid,
											(Swaf.BGuid.Guid)(existArt == null ? m_idDict[article.get("id") as Swaf.BGuid.Guid] : existArt.get("objectId")));

									if (!m_idDict.ContainsKey(article.get("parentHostId") as Swaf.BGuid.Guid))
										m_idDict.Add(article.get("parentHostId") as Swaf.BGuid.Guid,
											(Swaf.BGuid.Guid)(existArt == null ? existParent == null ? m_idDict[article.get("id") as Swaf.BGuid.Guid] : existParent.get("parentHostId") : existArt.get("parentHostId")));

									if (article.get("rootId") != null && !m_idDict.ContainsKey(article.get("rootId") as Swaf.BGuid.Guid))
										m_idDict.Add(article.get("rootId") as Swaf.BGuid.Guid,
											(Swaf.BGuid.Guid)(existArt == null ? existParent == null ? m_idDict[article.get("id") as Swaf.BGuid.Guid] : existParent.get("rootId") : existArt.get("rootId")));

									article.put("id", m_idDict[article.get("id") as Swaf.BGuid.Guid]);
									article.put("parentId", m_idDict[article.get("parentId") as Swaf.BGuid.Guid]);
									article.put("objectId", m_idDict[article.get("objectId") as Swaf.BGuid.Guid]);
									article.put("parentHostId", m_idDict[article.get("parentHostId") as Swaf.BGuid.Guid]);
									if(article.get("rootId") != null)
										article.put("rootId", m_idDict[article.get("rootId") as Swaf.BGuid.Guid]);
									article.put("companyId", restoreCompany.get("id"));
									if (existArt != null)
										article.put("treeName", existArt.get("treeName"));
									article.isNew = existArt == null;
									if (existArt != null && parentId != null && ((IComparable)parentId).CompareTo(existArt.get("parentId")) == 0)
										article.setDirtyFlag("parentId", false);
									if (existArt != null && ((IComparable)article.get("baseTitle")).CompareTo(existArt.get("baseTitle")) == 0)
										article.setDirtyFlag("baseTitle", false);

									context.put("doingArticleRestore", true);
									context.put("delayTranslations", true);
									context.RuleGroup.execute(context, "updateArticle", article);
								}
							}
						}
					}
					catch (Exception e)
					{
						throw new ArticlesException("error executing restoreBackUpOnArticles", e);
					}
				}
			}
			finally
			{
				ActiveCompanyId = oldCmpId;
			}

		}

		public object getArticleBackupRestoreTemplate(IApplication app, IRuleContext context, object resultValue, object[] inputParams)
		{
			string template = "Articles.Gui.AspNet.BaseDeploymentOptions";
			if (resultValue != null)
				return resultValue.ToString().Length == 0 ? template
					: new StringBuilder(50).Append(resultValue.ToString()).Append(",").Append(template).ToString();
			return template;
		}

		/// <summary>
		/// helper method for persisting article translation
		/// </summary>
		/// <param name="field"></param>
		/// <param name="updatedfield"></param>
		/// <param name="article"></param>
		/// <param name="lang"></param>
		protected void calcArticleLangTranslation(ref IBizObj article, IBizObj translation)
		{
			string[] fields = new string[] { "translatedTitles", "translatedSummaries", "translatedBodies" };
			string[] updatedfields = new string[] { "titleTranslation", "summaryTranslation", "bodyTranslation" };
			for (int i = 0; i < fields.Length; i++)
			{
				IBizObjList list = article.get(fields[i]) as IBizObjList;
				if (list != null)
				{
					IBizObj bizobj = list.search("i", translation.get("id"));
					if (bizobj == null)
					{
						bizobj = ((IBizObjList)article.get(fields[i])).insertNew();
						bizobj.put("i", translation.get("id"));
						bizobj.put("l", translation.get("language.id"));
						bizobj.put("r", translation.get("language.regionCode"));
						bizobj.put("cd", DateTime.Now.ToUniversalTime());
						bizobj.put("ci", CurrentUser.get("id"));
					}
					bizobj.put("ud", DateTime.Now.ToUniversalTime());
					bizobj.put("ui", CurrentUser.get("id"));
					bizobj.put("v", article.get(updatedfields[i]));
				}
			}
		}
		
    }
}